package dijjer.io.xfer.http.handlers;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import dijjer.AbstractFactory;
import dijjer.Dijjer;
import dijjer.io.comm.AbstractPeer;
import dijjer.io.comm.AbstractRoutingTable;
import dijjer.io.comm.WebPeer;
import dijjer.io.download.Download;
import dijjer.io.download.DownloaderThread;
import dijjer.io.store.DataStore;
import dijjer.io.store.HashStore;
import dijjer.io.store.Store;
import dijjer.io.xfer.PacketThrottle;
import dijjer.io.xfer.http.HTTPResponse;
import dijjer.io.xfer.http.HTTPServer;
import dijjer.io.xfer.http.PageHandler;
import dijjer.util.logging.Level;
import dijjer.util.logging.LogHandler;
import dijjer.util.logging.Logger;

public class StatusPageHandler extends TemplatePageHandler {

	public static final String VERSION = "$Id: StatusPageHandler.java 34 2009-03-20 13:27:26Z mathias.demare $";
	private static final String TEMPLATE_PAGE = "templates/default.html";
	private static final String BUILD = "build";
	private static final String TITLE = "title";
	private static final String BODY = "body";
	private static final String INFO = "info";
	private static final String EXTERNAL_ADDRESS = "external_address";
	private static final String CACHE_SIZE = "cache_size";
	private static final String MAX_CACHE_SIZE = "max_cache_size";
	private static final String HASH_SIZE = "hash_size";
	private static final String MAX_HASH_SIZE = "max_hash_size";
	private static final String CONNECTIONS = "connections";
	private static final String ACTIVE_DOWNLOADS = "active_downloads";
	private static final String RECENT_DOWNLOADS = "recent_downloads";
	private static final String DOWNLOADER_THREADS = "downloader_threads";
	private static final String RECENT_ERRORS = "recent_errors";
	private static final String RECENT_EVENTS = "recent_events";
	private static final int MAX_EVENTS = 10;
	private static final ArrayList _events = new ArrayList();
	private static final ArrayList _errors = new ArrayList();
	private static final LogHandler _eventHandler = new LogHandler() {

		public boolean isActive() {
			return true;
		}

		public void log(Level level, Date time, String message, String sourceClass, String sourceMethod,
				int sourceLine, Throwable thrown) {
			synchronized (_events) {
				while (_events.size() >= MAX_EVENTS) {
					_events.remove(0);
				}
			}
			synchronized (_errors) {
				while (_errors.size() >= MAX_EVENTS) {
					_errors.remove(0);
				}
			}
			String tag = "";
			String closeTag = "";
			if (level.isError()) {
				tag = "<font color=red>";
				closeTag = "</font>";
			}
			String event = "<li>" + tag + "[" + time + "] " + message + closeTag + "<br>";
			// include more info?
			if (level.isError()) {
				synchronized (_errors) {
					_errors.add(event);
				}
			} else {
				synchronized (_events) {
					_events.add(event);
				}
			}
		}

		public void setActive(boolean active) {
		}
	};
	static {
		Logger.addHandler(Level.INFO, null, _eventHandler);
		Logger.addHandler(Level.WARNING, null, _eventHandler);
		Logger.addHandler(Level.ERROR, null, _eventHandler);
		Logger.addHandler(Level.FATAL, null, _eventHandler);
	}

	public StatusPageHandler() {
		super(getDefaultTemplatePage());
		setTemplateField(BUILD, String.valueOf(Dijjer.BUILD));
		setTemplateField(TITLE, "Status Page");
	}

	public HTTPResponse handleRequest(String method, String url, Map params, Map headers, Reader reader) {
		TemplateResponse response = (TemplateResponse) super.handleRequest(method, url, params, headers, reader);
		StringBuffer info = new StringBuffer();
		if (params.get("debug") != null) {
			info.append("<p>" + "<table border=\"1\">" + "<tr><td>METHOD</td><td>" + method + "</td></tr>"
					+ "<tr><td>URL</td><td>" + url + "</td></tr>" + "<tr><td>PARAMS</td><td>" + params + "</td></tr>"
					+ "<tr><td>HEADERS</td><td>" + headers + "</td></tr>" + "</table>" + "</p>\n");
		}
		info.append(getFoundNewBuild());
		response.setPageField(INFO, info.toString());
		AbstractRoutingTable rt = AbstractFactory.getFactory().getRoutingTable();
		String localhost = rt.getPeer().toString();
		response.setPageField(EXTERNAL_ADDRESS, "<a href=" + HTTPServer.getHTTPServer().getURL() + ">" + localhost
				+ "</a>");
		response.setPageField(CACHE_SIZE, String.valueOf(DataStore.getDataStore().getCacheSize()
				* Store.DATA_BLOCK_SIZE / (1024 * 1024)));
		response.setPageField(MAX_CACHE_SIZE, String.valueOf(DataStore.getDataStore().getMaxBlocks()
				* Store.DATA_BLOCK_SIZE / (1024 * 1024)));
		response.setPageField(HASH_SIZE, String.valueOf(HashStore.getHashStore().getCurrentSize()));
		response.setPageField(MAX_HASH_SIZE, String.valueOf(HashStore.getHashStore().getMaxBlocks()));
		response.setPageField(CONNECTIONS, getConnections());
		response.setPageField(RECENT_DOWNLOADS, getRecentDownloads());
		response.setPageField(ACTIVE_DOWNLOADS, getActiveDownloads());
		response.setPageField(RECENT_ERRORS, getRecentErrors());
		response.setPageField(RECENT_EVENTS, getRecentEvents());
		return response;
	}

	private String getRecentDownloads() {
		if (DownloadResponse.recentDownloads.isEmpty()) {
			return "No recent downloads";
		}
		StringBuffer ret = new StringBuffer(1000);
		ret.append("<table border=\"1\">");
		ret.append(DownloadResponse.RECENT_DOWNLOADS_HEADER);
		synchronized (DownloadResponse.recentDownloads) {
			for (Iterator i = DownloadResponse.recentDownloads.iterator(); i.hasNext();) {
				String rd = (String) i.next();
				ret.append(rd);
			}
		}
		ret.append("</table>");
		return ret.toString();
	}
	
	private String getActiveDownloads() {
		LinkedList jobs = DownloaderThread.getActiveJobs();
		if (jobs.isEmpty()) {
			return "No active downloads";
		} else {
			StringBuffer sb = new StringBuffer();
			sb
					.append("<table border=\"1\"><tr><td><b>File</b></td><td><b>Length</b></td><td><b>% Downloaded</b></td><td><b>% Sent to Client</b></td><td><b>% from cache</b></td></tr>");
			for (Iterator i = jobs.iterator(); i.hasNext();) {
				Download dl = (Download) i.next();
				sb.append("<tr><td>" + dl.getFileName() + "</td>");
				sb.append("<td>" + dl.getLength() + "</td>");
				sb.append("<td>" + Math.min(100, (dl.getDownloaded() * 100) / dl.getLength()) + "</td>");
				sb.append("<td>" + ((dl.getSent() * 100) / dl.getLength()) + "</td>");
				if (dl.getSent() > 0) {
					sb.append("<td>" + Math.min(100, (dl.getBytesFromCache() * 100) / dl.getDownloaded()) + "</td>");
				} else {
					sb.append("<td>0</td>");
				}
				sb.append("</tr>");
			}
			sb.append("</table>");
			return sb.toString();
		}
	}

	private String getFoundNewBuild() {
		StringBuffer output = new StringBuffer();
		if ((!AbstractFactory.getFactory().getRoutingTable().isUserNotifiedOfNewsestSeenBuild())
				&& (AbstractFactory.getFactory().getRoutingTable().getNewestSeenBuild() > Dijjer.BUILD)) {
			output.append("Your current build is " + Dijjer.BUILD + " but peers of build "
					+ AbstractFactory.getFactory().getRoutingTable().getNewestSeenBuild() + " have been seen in the network.<p>");
			output.append("You should upgrade Dijjer from "
					+ "<a href=\"http://dijjer.org/\">dijjer.org</a> as soon as possible.<p>");
			output.append("You can also request the content using this build by hitting the "
					+ "<b>Refresh</b> button in your browser.");
			AbstractFactory.getFactory().getRoutingTable().setUserNotifiedOfNewsestSeenBuild(true);
		}
		return output.toString();
	}

	private String getRecentEvents() {
		StringBuffer output = new StringBuffer();
		output.append("<ul>");
		for (Iterator i = ((List) _events.clone()).iterator(); i.hasNext();) {
			String event = (String) i.next();
			if (event != null) {
				output.append(event);
			}
		}
		output.append("</ul>");
		return output.toString();
	}

	private String getRecentErrors() {
		StringBuffer output = new StringBuffer();
		output.append("<ul>");
		for (Iterator i = ((List) _errors.clone()).iterator(); i.hasNext();) {
			String error = (String) i.next();
			if (error != null) {
				output.append(error);
			}
		}
		output.append("</ul>");
		return output.toString();
	}

	private String getConnections() {
		StringBuffer output = new StringBuffer();
		if (AbstractFactory.getFactory().getRoutingTable().getPeers().size() > 0) {
			output.append("<table border=\"1\"><tr>" + "<th>Peer</th>" + "<th>Port</th>" + "<th>Build</th>"
					+ "<th>Throttle</th>" + "</tr>");
			for (Iterator i = AbstractFactory.getFactory().getRoutingTable().getPeers().iterator(); i.hasNext();) {
				AbstractPeer p = (AbstractPeer) i.next();
				AbstractRoutingTable.PeerInfo pi = AbstractFactory.getFactory().getRoutingTable().getPeerInfo(p);
				if (p != null) {
					if (Dijjer.getDijjer().getSeedNodes().contains(p)) {
						output.append("<tr><td><font color=\"green\">" + p.toString()
								+ "</font></td><td><font color=\"green\">" + "</font></td>");
					} else {
						output.append("<tr><td>" + p.toString() + "</td><td>" + "</td>");
					}
					if (pi != null) {
						output.append("<td>" + pi.getBuild() + "</td>");
					} else {
						output.append("<td>Unknown</td>");
					}
					output.append("<td>" + PacketThrottle.getThrottle(p) + "</td>");
					output.append("</tr>");
				}
			}
			output.append("</table>");
		} else {
			output.append("Not connected to any peers.");
		}
		return output.toString();
	}

	private static String getDefaultTemplatePage() {
		BufferedReader reader = new BufferedReader(new InputStreamReader(PageHandler.class
				.getResourceAsStream(TEMPLATE_PAGE)));
		StringWriter writer = new StringWriter();
		int len;
		char[] chars = new char[256];
		try {
			while ((len = reader.read(chars)) != -1) {
				writer.write(chars, 0, len);
			}
		} catch (IOException e) {
			writer.flush();
			writer.write("\n<BR>ERROR DURING READ!<BR>\n");
			e.printStackTrace(new PrintWriter(writer));
		}
		return writer.toString();
	}
}
